﻿using System;
using System.Collections.Generic;
using System.Windows;
using AzukiMap.Layer;
using AzukiMap.Tools;
using GeoAPI.Geometries;
using Point=GisSharpBlog.NetTopologySuite.Geometries.Point;

namespace AzukiMap.Renderer
{
    public class RasterRenderer : Renderer
    {
        private readonly RasterRendererCore _simpleRenderer;
        private readonly RasterRendererCore _transformRenderer;
        private readonly RasterRendererCore _triangleRenderer;
        private double _rendererAccuracyPixel = 1.0;

        public RasterRenderer(MapLayer layer)
            : base(layer)
        {
            _simpleRenderer = new SimpleRenderer(layer)
                                  {
                                      RendererAccuracyPixel = _rendererAccuracyPixel
                                  };
            _triangleRenderer = new TrianglePrimitiveRenderer(layer)
                                    {
                                        RendererAccuracyPixel = _rendererAccuracyPixel
                                    };
            _transformRenderer = new TransformRenderer(layer)
                                     {
                                         RendererAccuracyPixel = _rendererAccuracyPixel
                                     };
        }

        public double RendererAccuracyPixel
        {
            get { return _rendererAccuracyPixel; }
            set
            {
                _rendererAccuracyPixel = value;
                _simpleRenderer.RendererAccuracyPixel = value;
                _triangleRenderer.RendererAccuracyPixel = value;
                _transformRenderer.RendererAccuracyPixel = value;
            }
        }

        public IList<FrameworkElement> Draw(Uri imageUri, IPolygon imagePolyOnMap)
        {
            if ( imagePolyOnMap.SRID <= 0 )
                imagePolyOnMap.SRID = _layer.Srid;
            ImageMidPoints midPoints = GetImageMidPoints(imagePolyOnMap);
            IPolygon imageBoundaryOnMap = GetBoundaryOnMap(imagePolyOnMap);

            if (_simpleRenderer.IsFitToDraw(imageBoundaryOnMap, midPoints))
            {
                return _simpleRenderer.CreateImages(imageUri, imageBoundaryOnMap);
            }
            if (_triangleRenderer.IsFitToDraw(imageBoundaryOnMap, midPoints))
            {
                return _triangleRenderer.CreateImages(imageUri, imageBoundaryOnMap);
            }
            /*
            if (_transformRenderer.IsFitToDraw(imageBoundaryOnMap, midPoints))
            {
                _transformRenderer.CreateImages(imageUri, imageBoundaryOnMap);
                return;
            }
             */
            return _triangleRenderer.CreateImages(imageUri, imageBoundaryOnMap);
        }


        /// <summary>
        /// Get source and projected middle points on image edges to select a suitable renderer.
        /// </summary>
        /// <param name="imagePoly"></param>
        /// <returns></returns>
        private ImageMidPoints GetImageMidPoints(IPolygon imagePoly)
        {
            IPoint sourceTL = imagePoly.ExteriorRing.GetPointN(0);
            IPoint sourceBL = imagePoly.ExteriorRing.GetPointN(1);
            IPoint sourceBR = imagePoly.ExteriorRing.GetPointN(2);
            IPoint sourceTR = imagePoly.ExteriorRing.GetPointN(3);
            sourceTL.SRID = imagePoly.SRID;
            sourceBL.SRID = imagePoly.SRID;
            sourceBR.SRID = imagePoly.SRID;
            sourceTR.SRID = imagePoly.SRID;

            IPoint sourceCL = GetMiddlePoint(sourceTL, sourceBL);
            IPoint sourceBC = GetMiddlePoint(sourceBR, sourceBL);
            IPoint sourceCR = GetMiddlePoint(sourceTR, sourceBR);
            IPoint sourceTC = GetMiddlePoint(sourceTR, sourceTL);
            IPoint sourceCC = GetMiddlePoint(sourceCR, sourceCL);

            var sp = new ImageMidPoints
                         {
                             onMapCL = GetPointOnMap(sourceCL),
                             onMapBC = GetPointOnMap(sourceBC),
                             onMapCR = GetPointOnMap(sourceCR),
                             onMapTC = GetPointOnMap(sourceTC),
                             onMapCC = GetPointOnMap(sourceCC)
                         };

            return sp;
        }

        private IPoint GetMiddlePoint(IPoint p1, IPoint p2)
        {
            double x = (p1.X + p2.X) / 2;
            double y = (p1.Y + p2.Y) / 2;
            var p = new Point(x, y) {SRID = p1.SRID};

            return p;
        }

        /// <summary>
        /// Get a projected point as a map projection from a raster source projection.
        /// </summary>
        /// <param name="sourcePoint">coord by a raster source projection</param>
        /// <returns>coord as a map projection</returns>
        private IPoint GetPointOnMap(IPoint point)
        {
            return TransformUtil.Transform(point, _layer.MapCoord.Srid);
        }

        /// <summary>
        /// Get a projected image boundary as the map projection from on the raster source projection;
        /// </summary>
        /// <param name="sourceBoundary"></param>
        /// <returns>polygon as the map projection</returns>
        private IPolygon GetBoundaryOnMap(IPolygon sourceBoundary)
        {
            return TransformUtil.Transform(sourceBoundary, _layer.MapCoord.Srid);
        }
    }

    public class ImageMidPoints
    {
        public IPoint onMapBC;
        public IPoint onMapCC;
        public IPoint onMapCL;
        public IPoint onMapCR;
        public IPoint onMapTC;
    }
}